import java.util.*;
import java.io.*;


import javax.xml.parsers.DocumentBuilderFactory;


import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class RunMe {

	private static HashMap nameTOquerynode=new HashMap();
	public static void main(String[] arg)
	{
		String database;
		double Tc, Ts;
		List input=new ArrayList();
		Scanner scan = new Scanner(System.in);
		System.out.println("please input the url of the database, Coverage, and Specification divided by space (e.g. input: \"java.sun.com 100 0.6\")");
		StringTokenizer st = new StringTokenizer(scan.nextLine());
		while(st.hasMoreTokens())
		{
		input.add(st.nextToken());
		}
		String s[] = (String[])input.toArray(new String[input.size()]);
		database=s[0];
		Tc=Double.parseDouble(s[1]);
		Ts=Double.parseDouble(s[2]);
		
		File xmlfile=new File("classification.xml");
		if(xmlfile.exists())
		{
			try{
			Document xml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlfile);
			Node alldata = xml.getFirstChild();
			buildTree(alldata);
			}catch(Exception e){System.err.println(e);}
		}
		else
		{
			System.err.println("The classification.xml file should be put in "+xmlfile.getAbsolutePath());
			System.exit(0);
		}
		classification.nameTOquerynode=nameTOquerynode;
		String result=classification.classify(Tc,Ts,database);
		
		TreeSet visitednodes=new TreeSet();
		
		System.out.println("The "+database+" is classified in following categories:");
		//if(result.equals("Root")){System.out.println("Root/");System.exit(0);}
		StringTokenizer subc=new StringTokenizer(result);
		
		while(subc.hasMoreTokens())
		{
			String str=subc.nextToken();
			//System.out.println(str+"--");
			if(str.split("/")[0].equals("Root"))
			{
				System.out.println(str);
				String[] spit=str.split("/");
			    for(int i=0;i<spit.length;i++)
			    {
			    	visitednodes.add(spit[i]);
			    }
			
			}
			else
			{
                
                StringBuffer sb=new StringBuffer("");
                String[] spit2=str.split("/");
                
                for(int i=1;i<spit2.length;i++)
                {
                	visitednodes.add(spit2[i]);
                }
                
                String x=spit2[1];
                while(!x.equals("Root"))
                {
                x=(String)classification.parentstree.get(x);
                if(x.equals("Root"))sb.insert(0, x);
                else sb.insert(0, "/"+x);
                }
                System.out.println(sb.append(str));
			}
					
		}
		System.out.println("------------------------------------------------------");
		System.out.println("creating files...");
		Iterator itr=visitednodes.iterator();
		
		while(itr.hasNext())
		{
			String tp=(String)itr.next();
			if(((String[])classification.databasetree.get(tp)).length==0)
				continue;
			HashSet searchedresults=new HashSet();
			System.out.print("from "+tp+" :");
			searchedresults=classification.getallresults(tp, visitednodes);
			System.out.println("");
			Presult conbinedresults=classification.combinecoverage(searchedresults);
			Summary sum=BuildSummary.build(tp, conbinedresults);
			print(sum,database);
			
		}
	}
	
	private static void print(Summary sum, String database)
	{
		
		try
		{
			FileOutputStream oos=new FileOutputStream(sum.getName()+"-"+database+".txt",true);
			Writer out = new BufferedWriter(new OutputStreamWriter(oos));
			List terms=new ArrayList();
		    Iterator it=sum.TermsIteration();
		    while(it.hasNext())
		    {
		    	String term=(String)it.next();
		    	terms.add(term);
		    	
		    }
		    String[] inoder=(String[])terms.toArray(new String[terms.size()]);
		    Arrays.sort(inoder);
		    for(int i=0;i<inoder.length;i++)
		    {
		    	out.write("Term: "+inoder[i]+" DocumentFrequency: "+sum.getDfbyTerm(inoder[i])+"\n");
		    }
		    out.flush();
		    oos.close();
		}catch(Exception e){System.err.println(e);}
		
		
	}
	
	private static void buildTree(Node categories)
	{
		Node entry,queryentry, singlequery;
		NodeList children, querychildren, allqueries; 
		
		if(categories==null)
			return;
		
		children=categories.getChildNodes();
		for(int i=0;i<children.getLength();i++)
		{   QueryNode parent, child;
			entry=children.item(i);
			if(entry.getNodeName().equals("category"))
			{
				String nodename=entry.getAttributes().getNamedItem("name").getNodeValue();
				if(nameTOquerynode.containsKey(nodename))
				{
					parent=(QueryNode)nameTOquerynode.get(nodename);
				}
				else
				{
					parent=new QueryNode(nodename);
					nameTOquerynode.put(nodename, parent);
				}
			}
			else continue;
			
			querychildren=entry.getChildNodes();
			for(int j=0;j<querychildren.getLength();j++)
			{
				queryentry=querychildren.item(j);
				if(queryentry.getNodeName().equals("queries"))
				{
					allqueries=queryentry.getChildNodes();
					for(int k=0;k<allqueries.getLength();k++)
					{
						singlequery=allqueries.item(k);
					    if(singlequery.getNodeName().equals("query"))
					    {
					     String queryname=singlequery.getAttributes().getNamedItem("subcategory").getNodeValue();
					      if(nameTOquerynode.containsKey(queryname))
					      {
					    	  child=(QueryNode)nameTOquerynode.get(queryname);
					      }
					      else
					      {
					    	  child=new QueryNode(queryname);
					    	  nameTOquerynode.put(queryname, child);
					      }
					      parent.addMap(singlequery.getTextContent(), child);
					    }
					    else continue;
					    
					}
				}
				else if(queryentry.getNodeName().equals("categories"))
				{
					buildTree(queryentry);
				}
			}
			
			
		}
		
		
	}
}

class classification
{
	public static HashMap nameTOquerynode;
	public static HashMap databasetree=new HashMap();
	public static HashMap parentstree=new HashMap();
	public static HashMap TcMap=new HashMap();
	public static HashMap TsMap=new HashMap();
	public static HashMap categoryTOresults=new HashMap();
	
	public static String classify(double Tc, double Ts, String database)
	{
		databasetree.put("Root",new String[]{"Computers","Health","Sports"});
		databasetree.put("Computers", new String[]{"Hardware","Programming"});
		databasetree.put("Health",new String[]{"Fitness","Diseases"});
		databasetree.put("Sports", new String[]{"Basketball","Soccer"});
		databasetree.put("Hardware",new String[0]);
		databasetree.put("Programming",new String[0]);
		databasetree.put("Fitness",new String[0]);
		databasetree.put("Diseases",new String[0]);
		databasetree.put("Basketball",new String[0]);
		databasetree.put("Soccer",new String[0]);
		
		parentstree.put("Root", null);
		parentstree.put("Computers", "Root");
		parentstree.put("Health", "Root");
		parentstree.put("Sports", "Root");
		parentstree.put("Hardware", "Computers");
		parentstree.put("Programming", "Computers");
		parentstree.put("Fitness", "Health");
		parentstree.put("Diseases", "Health");
		parentstree.put("Basketball", "Sports");
		parentstree.put("Soccer", "Sports");
		
		
		    Iterator it =databasetree.keySet().iterator();
			while(it.hasNext())
			{
				String category=(String)it.next();
				Presult pr = createresult(category, database);
				TcMap.put(category, new Double(pr.getHit()));
				categoryTOresults.put(category, pr);
			}
			
			it =databasetree.keySet().iterator();
			while(it.hasNext())
			{
				String category=(String)it.next();
				double ts = calculateTS(category);
				TsMap.put(category, new Double(ts));
			}
			
			String classificationresult=createclassificationresult(Tc, Ts, database,"Root");
		   
			return ("Root"+classificationresult).trim();
		
	}
	
	static Presult createresult(String category, String database)
	{
		
		HashSet Presultset=new HashSet();
		if(category.equals("Computers")||category.equals("Health")||category.equals("Sports"))
		{
		QueryNode quenode = (QueryNode)nameTOquerynode.get("Root");
		 Iterator it = quenode.getQueries().iterator();
		 while(it.hasNext())
		 {
			String q = (String)it.next();
			if(((QueryNode)quenode.getNode(q)).getName().equals(category))
					{
				        Presult resultset=(new Query(q)).runquery(database);
				        if(resultset!=null)
				        {
				        	Presultset.add(resultset);
				        }else{System.err.println("query :"+q+" in "+category+"has null result");}
					}
		 }
		}
		
		else if(category.equals("Hardware")||category.equals("Programming"))
		{
		QueryNode quenode = (QueryNode)nameTOquerynode.get("Computers");
		 Iterator it = quenode.getQueries().iterator();
		 while(it.hasNext())
		 {
			String q = (String)it.next();
			if(((QueryNode)quenode.getNode(q)).getName().equals(category))
					{
				        Presult resultset=(new Query(q)).runquery(database);
				        if(resultset!=null)
				        {
				        	Presultset.add(resultset);
				        }else{System.err.println("query :"+q+" in "+category+"has null result");}
					}
		 }
		}
		
		else if(category.equals("Fitness")||category.equals("Diseases"))
		{
		 QueryNode quenode = (QueryNode)nameTOquerynode.get("Health");
		 Iterator it = quenode.getQueries().iterator();
		 while(it.hasNext())
		 {
			String q = (String)it.next();
			if(((QueryNode)quenode.getNode(q)).getName().equals(category))
					{
				        Presult resultset=(new Query(q)).runquery(database);
				        if(resultset!=null)
				        {
				        	Presultset.add(resultset);
				        }else{System.err.println("query :"+q+" in "+category+"has null result");}
					}
		 }
		}
		
		else if(category.equals("Basketball")||category.equals("Soccer"))
		{
		 QueryNode quenode = (QueryNode)nameTOquerynode.get("Sports");
		 Iterator it = quenode.getQueries().iterator();
		 while(it.hasNext())
		 {
			String q = (String)it.next();
			if(((QueryNode)quenode.getNode(q)).getName().equals(category))
					{
				        Presult resultset=(new Query(q)).runquery(database);
				        if(resultset!=null)
				        {
				        	Presultset.add(resultset);
				        }else{System.err.println("query :"+q+" in "+category+"has null result");}
					}
		 }
		}
		
		return combinecoverage(Presultset);
	}
	
	static Presult combinecoverage(HashSet pset)
	{
		List totalresult=new ArrayList();
		double totalhit=0;
		Iterator it=pset.iterator();
		while(it.hasNext())
		{
			Presult temp=(Presult)it.next();
			for(int i=0;i<temp.getSize();i++)
			{
				totalresult.add(temp.get(i));
				
			}
			totalhit+=temp.getHit();
		}
		return new Presult(totalresult,totalhit);
	}
	
	static double calculateTS(String category)
	{
		if(category.equals("Root"))
		{
			return 1.0;
		}
		
		double catcover=((Double)TcMap.get(category)).doubleValue();
		double allcover=0;
		String[] sibling=(String[])databasetree.get((String)parentstree.get(category));
		for(int i=0;i<sibling.length;i++)
		{
			allcover+=((Double)TcMap.get(sibling[i])).doubleValue();
		}
		
		return calculateTS((String)parentstree.get(category))*catcover/allcover;
	}
	
	static String createclassificationresult(double Tc, double Ts, String database, String parent)
	{
		String[] children=(String[])databasetree.get(parent);
		if(children.length==0)return " ";
		String rst="";
		for(int i=0;i<children.length;i++)
		{
			System.out.println("Category: "+children[i]+" Tc: "+((Double)TcMap.get(children[i])).doubleValue()+" Ts: "+((Double)TsMap.get(children[i])).doubleValue());
			if(((Double)TcMap.get(children[i])).doubleValue()>=Tc&&((Double)TsMap.get(children[i])).doubleValue()>=Ts)
			{
				rst+="/"+children[i];
				rst+=createclassificationresult(Tc, Ts, database, children[i]);
			}
		}
		return rst;
	}
	static HashSet getallresults(String tp, TreeSet visitednodes)
	{
		HashSet collect=new HashSet();
		String[] children=(String[])databasetree.get(tp);
		for(int i=0;i<children.length;i++)
		{   System.out.print(children[i]+" ");
			collect.add(categoryTOresults.get(children[i]));
			if(visitednodes.contains(children[i]))
			{
				collect.addAll(getallresults(children[i],visitednodes));
			}
		}
		return collect;
	}
} 
